/*
 * chat_connection.c
 *
 *  Created on: 2012-6-26
 *      Author: rendong
 */

#include <stdio.h>

#include "chat_connection.h"
#include "../core/chat_config.h"
#include "../os/chat_epoll.h"
#include "chat_message.h"
#include "../os/chat_file.h"
#include "../core/chat_log.h"

FILE *errorfp=NULL;


/**
 * 新用户连接
 */
void chat_client_handler_new(int fd, struct sockaddr_in *clientaddr,  chat_server_config_t * config)
{
	struct sockaddr_in *client;
	chat_session_t * session;

	session = (chat_session_t *) malloc(sizeof(chat_session_t));
	memset(session, 0, sizeof(chat_session_t));

	client = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
	memcpy(client, clientaddr, sizeof(struct sockaddr_in));

	session->sockfd = fd;
	session->config = config;
	session->clientaddr = client;

	session->read_buffer = malloc(SESSION_READ_BUFFER_SIZE);
	session->read_buffer_size = SESSION_READ_BUFFER_SIZE;
	session->read_buffer_data_size = 0;

	session->channel_name_size = 0;
	session->channel_name_list = (char **)malloc(sizeof(char *) * cfg_maxchannels);

	session->flag_close = 0;
	session->heart_time = 0;
	
	session->login_time = config->t_req_time;

	session->uid = NULL;
	session->nickname = NULL;

	if (config->fd_sessions[fd] != NULL){
		chat_log_error("session not close fd=%d\n", fd);
	}

	config->fd_sessions[fd] = session;

	if (session->config->chat_phplog_fd != -1){
		char * buff_log = malloc(SIZE_LOG_BUFFER);
		int log_n = snprintf(buff_log, SIZE_LOG_BUFFER,
				"%s [conn] online=%d fd=%d\r\n",
				session->config->s_req_time, config->cur_clients,  session->sockfd);
		chat_file_write(session->config->chat_phplog_fd, buff_log, log_n);
	}
}

/**
 * 处理数据包
 */
void chat_client_handler_read(chat_session_t *session)
{
	int pack_len;
	short pack_type;
	char *pack_buffer;

	while (session->read_buffer_data_size >= INT32_LEN+INT16_LEN){
		pack_len = *(int *)session->read_buffer;
		pack_type = *(short*)(session->read_buffer + INT32_LEN);

		// 如果数据包出错，pack_len可能小于0，导致内存溢出错误
		if (pack_len >= 0 && session->read_buffer_data_size >= (pack_len + INT32_LEN)){
			pack_buffer = (char *)(session->read_buffer + INT32_LEN + INT16_LEN);


			chat_log_debug("handler_message start uid=%s name=%s fd=%d len=%d type=%d\n", session->uid, session->nickname, session->sockfd, pack_len-INT16_LEN, pack_type);
			chat_handle_message(session, pack_type, pack_len-INT16_LEN, pack_buffer);
			chat_log_debug("handler_message end   uid=%s name=%s fd=%d len=%d type=%d\n", session->uid, session->nickname, session->sockfd, pack_len-INT16_LEN, pack_type);

			session->read_buffer_data_size -= INT32_LEN + pack_len;

			// 内存移动
			if (session->read_buffer_data_size > 0)
				memmove(session->read_buffer,
						session->read_buffer+INT32_LEN+pack_len,
						session->read_buffer_data_size);
		}else{
			chat_log_debug("break fd=%d rbds:%d, pack_len:%d\n", session->sockfd, session->read_buffer_data_size, pack_len);
			break;
		}
	}
}

/**
 * 关闭连接处理
 */
void chat_client_handler_close(int fd, chat_server_config_t * config)
{
	chat_session_t * session, *fsession;
	chat_send_item_t *head, *sitem;
	chat_list_t *list;
	char *fuid, *fret_buffer, *channel_name;
	int i, str_len,fret_len;

	session = config->fd_sessions[fd];

	if (session != NULL)
	{
		if (session->uid != NULL && session->nickname != NULL && strncmp(session->uid, "php", 3) != 0)
		{
			char * buff_log = malloc(SIZE_LOG_BUFFER);
			int log_n = snprintf(buff_log, SIZE_LOG_BUFFER,
					"%s [下线] online=%d uid=%s nick=%s fd=%d\r\n",
					session->config->s_req_time, config->cur_clients, session->uid, session->nickname, session->sockfd);
			chat_file_write(session->config->chat_log_fd, buff_log, log_n);
		}

		// 对好友发送下线通知
		if (session->friend_len > 0 && session->friend_list!= NULL && session->uid != NULL){
			for(i = 0; i < session->friend_len; i++){
				fuid = session->friend_list[i];
				if (fuid == NULL)continue;

				fsession = chat_hash_get(config->uid_sessions, fuid);
				if (fsession != NULL && strcmp(fuid, session->uid) != 0){
					str_len = strlen(session->uid);
					fret_len = INT32_LEN + INT16_LEN + INT32_LEN + str_len;
					fret_buffer = malloc(fret_len);

					*(int *)fret_buffer = INT16_LEN + INT32_LEN + str_len;
					*(short *)(fret_buffer+INT32_LEN) = 106;
					*(int *)(fret_buffer+INT32_LEN+INT16_LEN) = str_len;
					memcpy(fret_buffer + INT32_LEN+INT16_LEN+INT32_LEN, session->uid, str_len);

					chat_send(fsession, fret_buffer, fret_len, 1);
				}

				free(fuid);
			}
		}

		// 删除加入的频道
		if (session->channel_name_size > 0){
			for(i=0; i<session->channel_name_size;i++){
				channel_name = session->channel_name_list[i];
				if (channel_name != NULL){
					list = chat_hash_get(config->all_channel, channel_name);
					chat_list_del(list, session);
					free(channel_name);
				}
			}
		}
		free(session->channel_name_list);

		if (session->friend_list != NULL)
			free(session->friend_list);

		if (session->uid != NULL){
			chat_hash_del(config->uid_sessions, session->uid);
			free(session->uid);
		}

		if (session->nickname != NULL)
			free(session->nickname);

		free(session->read_buffer);

		if (session->clientaddr != NULL)
			free(session->clientaddr);

		// free send list
		head = session->send_head;
		while(head != NULL){
			if (head->is_free)
				free(head->buffer);

			sitem = head;
			head = head->next;

			free(sitem);
		}


		free(session);
		session = NULL;
	}

	config->fd_sessions[fd] = NULL;
}


/**
 * 开启服务端
 */
void chat_start_chat_server()
{
	chat_server_config_t srv_config;
	chat_session_t *sessions[cfg_maxclients + 10];
	chat_hash_table_t uid_sessions;
	chat_hash_table_t all_channel;
	chat_hash_table_t uid_disable;
	int chat_log_fd, chat_phplog_fd,i;

	chat_phplog_fd = -1;

	for(i=0; i< cfg_maxclients+10; i++){
		sessions[i] = NULL;
	}

	chat_log_fd = open(cfg_log_fname, O_CREAT|O_WRONLY|O_APPEND, S_IRWXU|S_IRWXG|S_IROTH);

	if (chat_log_fd == -1){
		printf("open file error %d %s\n", errno, strerror(errno));
		exit(0);
	}

	if (cfg_phplog_fname != NULL){
		chat_phplog_fd = open(cfg_phplog_fname, O_CREAT|O_WRONLY|O_APPEND, S_IRWXU|S_IRWXG|S_IROTH);

		if (chat_phplog_fd == -1){
			printf("open file error %d %s\n", errno, strerror(errno));
			exit(0);
		}
	}


	errorfp = fopen(cfg_errorlog_fname, "a+");
	if (errorfp == NULL){
		printf("open errorlog file error %d %s\n", errno, strerror(errno));
		exit(0);
	}

	chat_hash_init(&uid_sessions, cfg_maxclients);
	chat_hash_init(&all_channel, 1024);
	chat_hash_init(&uid_disable, cfg_maxclients / 10);

	srv_config.srv_addr = cfg_local_addr;
	srv_config.srv_port = cfg_port;
	srv_config.cfg_max_clients = cfg_maxclients;
	srv_config.stat_max_fd = 0;
	srv_config.stat_max_clients = 0;
	srv_config.cur_clients = 0;
	srv_config.chat_log_fd = chat_log_fd;
	srv_config.chat_phplog_fd = chat_phplog_fd;

	srv_config.fd_sessions = (chat_session_t **)&sessions;
	srv_config.uid_sessions = &uid_sessions;
	srv_config.all_channel = &all_channel;
	srv_config.uid_disable = &uid_disable;

	srv_config.handler_new = chat_client_handler_new;
	srv_config.handler_read = chat_client_handler_read;
	srv_config.handler_close = chat_client_handler_close;

	srv_config.s_req_time = (char *)malloc(SIZE_REQ_TIME);

	// socket主循环
	chat_epoll_socket_start(&srv_config);

	free(srv_config.s_req_time);
	close(chat_log_fd);
}
